using System;
using System.Collections.Generic;

namespace Framework.Core
{
    public delegate void EventHandler(IEventData eventData);
    public class EventDispatcher
    {
        internal class DispatchEventData
        {
            public string EventType;
            public IEventData EventData;
            public List<EventHandler> Handlers;
            public void Execute()
            {
                if (EventData != null)
                {
                    ReferencePool.Release(EventData);
                }
                if (Handlers == null || Handlers.Count < 1) return;

            }
        }
        private static readonly CustomPool<List<EventHandler>> _handlerListPool = new CustomPool<List<EventHandler>>(() => new List<EventHandler>());
        private static readonly CustomPool<DispatchEventData> _dispatchDataPool = new CustomPool<DispatchEventData>(() => new DispatchEventData());
        private readonly Dictionary<string, List<EventHandler>> _handlers = new Dictionary<string, List<EventHandler>>();
        private EventDispatcher target;
        private Queue<DispatchEventData> _dispatchList;
        private bool _dispatching;
        public EventDispatcher(EventDispatcher target = null)
        {
            if (target == null)
            {
                target = this;
            }
            this.target = target;

        }
        public void AddEventListener(string eventType, EventHandler handler)
        {
            if (!target._handlers.TryGetValue(eventType, out List<EventHandler> handlers))
            {
                handlers = new List<EventHandler>();
                target._handlers.Add(eventType, handlers);
            }
            handlers.Remove(handler);
            handlers.Add(handler);
        }
        public void DispatchEvent(string type, bool immediate = true)
        {
            if (target._handlers.TryGetValue(type, out List<EventHandler> handlers))
            {
                var list = _handlerListPool.Get();
                list.Clear();
                list.AddRange(handlers);

                var item = _dispatchDataPool.Get();
                item.Handlers = list;
                item.EventType = type;
                item.EventData = null;
                if (immediate)
                {
                    DispatchEvent(item);
                    return;
                }
                _dispatchList ??= new Queue<DispatchEventData>();
                _dispatchList.Enqueue(item);
                if (_dispatching) return;
                _dispatching = true;
                CallNextFrame(DispatchEvent);
            }
        }
        public void DispatchEvent(IEventData e, bool immediate = true)
        {
            var type = e.EventType;
            if (target._handlers.TryGetValue(type, out List<EventHandler> handlers))
            {
                var list = _handlerListPool.Get();
                list.Clear();
                list.AddRange(handlers);

                var item = _dispatchDataPool.Get();
                item.Handlers = list;
                item.EventType = type;
                item.EventData = e;
                if (immediate)
                {
                    DispatchEvent(item);
                    return;
                }

                _dispatchList ??= new Queue<DispatchEventData>();
                _dispatchList.Enqueue(item);
                if (_dispatching) return;
                _dispatching = true;
                CallNextFrame(DispatchEvent);
            }
        }
        public void RemoveEventListener(string eventType, EventHandler handler)
        {
            if (!target._handlers.TryGetValue(eventType, out List<EventHandler> handlers))
            {
                return;
            }
            handlers.Remove(handler);
        }
        public bool HasEventListener(string eventType, EventHandler handler = null)
        {
            if (!target._handlers.TryGetValue(eventType, out List<EventHandler> handlers))
            {
                return false;
            }
            if (handler == null) return handlers.Count > 0;
            return handlers.Contains(handler);
        }
        public void RemoveAllEventListener()
        {
            target._handlers.Clear();
        }
        private void DispatchEvent(DispatchEventData value)
        {
            if (value == null) return;
            _dispatchDataPool.Release(value);
            var eventData = value.EventData;
            if (eventData != null)
            {
                ReferencePool.Release(eventData);
            }
            var handlers = value.Handlers;
            if (handlers == null || handlers.Count < 1) return;
            int len = handlers.Count;
            for (int i = 0; i < len; i++)
            {
                var item = handlers[i];
                if (item == null) continue;
                try
                {
                    item.Invoke(eventData);
                }
                catch (System.Exception e)
                {
                    FrameworkLog.LogError(e.ToString());
                }
            }
            handlers.Clear();
            _handlerListPool.Release(handlers);
        }
        private void DispatchEvent()
        {
            _dispatching = false;
            if (_dispatchList == null) return;
            var len = _dispatchList.Count;
            while (len-- > 0)
            {
                var item = _dispatchList.Dequeue();
                DispatchEvent(item);
            }
        }
        private void CallNextFrame(Action value)
        {
            EventDispatcherAsync.Add(value);
        }
    }
}